package org.mtmi.ui.controls;

import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Path;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.mtmi.io.IControlIO;
import org.mtmi.io.controls.SpinnerIO;
import org.mtmi.ui.GraphicUtil;
import org.mtmi.ui.M;
import org.mtmi.ui.controls.edits.SpinnerControlEditor;
import org.mtmi.ui.edit.IControlEditor;
import org.mtmi.ui.layouts.LayoutData;
import org.mtmi.ui.scenes.COLOR;
import org.mtmi.ui.scenes.FontManager;
import org.mtmi.ui.scenes.Scene;

/**
 * A spinner to choose and controlling value.
 * 
 */
public class Spinner extends AbstractSpinner implements IOrientable
{
	private int side_length; //width or length for '-', '+' area.
	
	public Spinner( Scene scene, int flags )
	{
		this( scene, 0,0,0, flags );
	}
	
	public Spinner( Scene scene, int valCurrent, int valMin, int valMax, int flags ) 
	{
		super(scene, valCurrent, valMin, valMax, flags);
		setBackground( COLOR.grey0 );
		setForeground( COLOR.grey100 );
		setSize( computeSize( new LayoutData(scene) ) );
	}
	
	@Override
	public boolean isHorizontal() {
		return (getFlags() & M.VERTICAL)==0;
	}
	
	@Override
	public void setHorizontal( boolean horizontal ) {
		if( horizontal!=isHorizontal() ) 
		{
			redraw(); //mark old bounds are to be redraw
			
			int w2 = size.x/2;
			int h2 = size.y/2;
			int cx = location.x+w2;
			int cy = location.y+h2;
			
			if( horizontal ) {
				flags &= ~M.VERTICAL; //reset flag
			} else {
				flags |= M.VERTICAL; //set flag
			}
			Point sz = computeSize( new LayoutData(getScene() ) );
			setBounds( cx-sz.x/2, cy-sz.y/2, sz.x, sz.y );
			
			redraw(); //redraw new bounds
		}
	}

	@Override
	protected boolean isNextValueAreaContains(int x, int y) {
		if( isHorizontal() ) {
			return GraphicUtil.rectContains( location.x+size.x-side_length, location.y, side_length, size.y, x, y );
		} else {
			return GraphicUtil.rectContains( location.x, location.y, size.x, side_length, x, y );
		}
	}

	@Override
	protected boolean isPreviousValueAreaContains(int x, int y) {
		if( isHorizontal() ) {
			return GraphicUtil.rectContains( location.x, location.y, side_length, size.y, x, y );
		} else {
			return GraphicUtil.rectContains( location.x, location.y+size.y-side_length, size.x, side_length, x, y );			
		}
	}

	@Override
	protected boolean isValueAreaContains(int x, int y) {
		if( isHorizontal() ) {
			return GraphicUtil.rectContains( location.x+side_length, location.y, size.x-2*side_length, size.y, x, y );
		} else {
			return GraphicUtil.rectContains( location.x, location.y+side_length, size.x, size.y-2*side_length, x, y );			
		}
	}
	
	@Override
	public void setBounds(int x, int y, int w, int h) {
		super.setBounds(x, y, w, h);
		//TODO: if font may changed updateMetrics(); -> side_width
	}

	private static final int MG=5;
	private static final String D="-"; //$NON-NLS-1$
	private static final String I="+"; //$NON-NLS-1$
	
	@Override
	public Point computeSize(LayoutData l) {
		l.gc.setFont( FontManager.getInstance().getNormalFont() );
		Point pn = l.gc.textExtent( D );
		Point px = l.gc.textExtent( I );
		String xv = worseDigitLength( getMinValue(), getMaxValue() );
		Point pv = l.gc.textExtent( xv );
		
		if( isHorizontal() ) {
			side_length = borderWidth+MG+Math.max( pn.x, px.x ) +MG+borderWidth/2;
			int hx = MG+Math.max( pn.y, Math.max( pv.y, px.y ))+MG+2*borderWidth;
			return new Point( side_length+borderWidth+MG+pv.x+MG+side_length, hx );
		} else {
			side_length = borderWidth+MG+Math.max( pn.y, px.y ) +MG+borderWidth/2;
			int wx = MG+Math.max( pn.x, Math.max( pv.x, px.x ))+MG + 2*borderWidth;
			return new Point( wx, side_length+borderWidth+MG+pv.x+MG+side_length );
		}
	}

	@Override
	public void paint( GC gc )
	{
		int bw2 = borderWidth/2;
		int r = Math.min( size.x, size.y )/4;
		gc.setForeground( getForeground() );
		gc.setBackground( getBackground() );
		gc.setLineWidth( borderWidth );
		Path p = GraphicUtil.getRoundRectangle( gc, location.x+bw2, location.y+bw2, size.x-borderWidth, size.y-borderWidth, r, r );
		gc.fillPath( p );
		gc.drawPath( p );
		
		boolean h = isHorizontal();
		
		// -, + sign
		gc.setFont( FontManager.getInstance().getNormalFont() );
		Point st = gc.textExtent( D );
		if( h ) {
			gc.drawText( D, location.x+(side_length-st.x)/2+bw2, location.y+(size.y-st.y)/2, true );
		} else {
			gc.drawText( D, location.x+(size.x-st.x)/2, location.y+size.y-side_length+(side_length-st.y)/2, true );
		}
		
		st = gc.textExtent( I );
		if( h ) {
			gc.drawText( I, location.x+size.x-side_length+(side_length-st.x)/2-bw2, location.y+(size.y-st.y)/2, true );
		} else {
			gc.drawText( I, location.x+(size.x-st.x)/2, location.y+(side_length-st.y)/2+bw2, true );		
		}
		
		//value:
		String s = Integer.toString( getValue() );
		st = gc.textExtent( s );
		if( h ) {
			gc.drawText( s, location.x+(size.x-st.x)/2, location.y+(size.y-st.y)/2, true );
		} else {
//TODO: really same ?			
			gc.drawText( s, location.x+(size.x-st.x)/2, location.y+(size.y-st.y)/2, true );
		}
		
		// vertical bar: separator:
		gc.setLineWidth( bw2 );
		int db = borderWidth+MG*2;
		if( h ) {
			int xl =  location.x+side_length+bw2;
			int yn = location.y+db, yx = location.y+size.y-db;
			if( yn < yx ) {
				gc.drawLine( xl, yn, xl, yx );
				xl = location.x+size.x-side_length-bw2;
				gc.drawLine( xl, yn, xl, yx );
			}
		} else {
			int yl = location.y+side_length-bw2;
			int xn = location.x+db;
			int xx = location.x+size.x-db;
			if( xn < xx ) {
				gc.drawLine( xn, yl, xx, yl );
				yl = location.y+size.y-side_length-bw2;
				gc.drawLine( xn, yl, xx, yl );
			}
		}
		
		p.dispose();
	}
	
	@Override
	public Object getAdapter( Class<?> clazz, Object arg ) {
		if( clazz == IControlEditor.class ) {
			return new SpinnerControlEditor( this );
		}
		if( clazz == IControlIO.class ) {
			return new SpinnerIO();
		}
		return super.getAdapter( clazz, arg );
	}

}
